package com.bjut.vendormgrsys.service.impl;

import com.bjut.vendormgrsys.constant.Constant;
import com.bjut.vendormgrsys.constant.Constant.AuditStatus;
import com.bjut.vendormgrsys.constant.Constant.CampaignStatus;
import com.bjut.vendormgrsys.model.domain.*;
import com.bjut.vendormgrsys.model.repository.*;
import com.bjut.vendormgrsys.service.CampaignService;
import com.bjut.vendormgrsys.util.DateUtils;
import com.bjut.vendormgrsys.util.JsonUtils;
import com.bjut.vendormgrsys.web.dto.Status;
import com.bjut.vendormgrsys.web.dto.campaign.*;
import com.bjut.vendormgrsys.web.dto.user.Body;
import com.bjut.vendormgrsys.web.dto.user.UserChildInfo;
import com.bjut.vendormgrsys.web.dto.user.UserInfo;
import com.bjut.vendormgrsys.web.dto.user.UserScoreInfo;
import lombok.extern.java.Log;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Log
@Service
public class CampaignServiceImpl implements CampaignService {
    @PersistenceContext
    private EntityManager entityManager;

    private final CampaignRepository campaignRepository;
    private final CampaignUserRepository campaignUserRepository;
    private final UserRepository userRepository;
    private final UserChildRepository userChildRepository;
    private final CampaignExpansionRepository campaignExpansionRepository;
    private final FileUtilService fileUtilService;

    @Autowired
    public CampaignServiceImpl(CampaignRepository campaignRepository, CampaignUserRepository campaignUserRepository, UserRepository userRepository,
            UserChildRepository userChildRepository,CampaignExpansionRepository campaignExpansionRepository, FileUtilService fileUtilService) {
        this.campaignRepository = campaignRepository;
        this.campaignUserRepository = campaignUserRepository;
        this.userRepository = userRepository;
        this.userChildRepository = userChildRepository;
        this.campaignExpansionRepository = campaignExpansionRepository;
        this.fileUtilService = fileUtilService;
    }

    /**
     * 保存活动
     * @param campaignInfo
     */
    @Override
    @Transactional
    public void saveCampaignPO(CampaignInfo campaignInfo) {
        String id = campaignInfo.getId();
        CampaignPO campaignPO = null;
        boolean isPersist = true;
        List<String> ignorePropList = new ArrayList<>();
        if (StringUtils.hasLength(id)) {
            Optional<CampaignPO> byId = campaignRepository.findById(id);
            if (byId.isPresent()) {
                ignorePropList.add("creatorId");
                ignorePropList.add("status");
                ignorePropList.add("auditsStatus");
                ignorePropList.add("dateCreated");

                campaignPO = byId.get();
                if(campaignPO.getAuditsStatus().equals(AuditStatus.PASS.getKey())){
                    throw new RuntimeException("审核通过后活动不可编辑！");
                }

                if(campaignPO.getAuditsStatus().equals(AuditStatus.NOTPASS.getKey())){
                    campaignPO.setAuditsStatus(AuditStatus.WAIT.getKey());
                    campaignPO.setStatus(CampaignStatus.NEW.getKey());
                    campaignPO.setReason(null);
                }
                isPersist = false;
            }
        }
        if (campaignPO == null) {
            campaignPO = new CampaignPO();
        }

        ignorePropList.add("lastUpdated");
        String[] ignoreProps = new String[ignorePropList.size()];
        ignorePropList.toArray(ignoreProps);
        BeanUtils.copyProperties(campaignInfo, campaignPO, ignoreProps);
        campaignPO.setLastUpdated(DateUtils.now());
        if (!isPersist) {
            entityManager.merge(campaignPO);
        } else {
            entityManager.persist(campaignPO);
        }
    }

    @Override
    public long findAll(final CampaignSearch body, List<CampaignInfo> list){
        int page = 1;
        int size = 10;
        Integer pageNum = body.getPageNum();
        if (pageNum != null) {
            page = pageNum;
        }
        page -= 1;
        Integer pageSize = body.getPageSize();
        if (pageSize != null) {
            size = pageSize;
        }
        // 根据动态条件从数据库分页查询用户信息
        Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "lastUpdated");

        Boolean mySignUp = body.getMySignUp();
        if(mySignUp && body.getCreatorId()!=null){
            List<CampaignUserPO> users = campaignUserRepository.findAllByUserId(body.getCreatorId());
            List<String> campaignIds = new ArrayList<>();
            if(users.size() > 0){
                for(CampaignUserPO user:users){
                    campaignIds.add(user.getCampaignId());
                }
            }
            if(campaignIds.size() == 0){
                return 0L;
            }
            body.setCampaignIds(campaignIds);
            body.setCreatorId(null);
        }

        Specification<CampaignPO> specification = (Specification<CampaignPO>) (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            String name = body.getName();
            if (StringUtils.hasLength(name)) {
                predicates.add(builder.like(root.get("name").as(String.class), "%" + name + "%"));
            }

            if(body.getCampaignIds()!=null && body.getCampaignIds().size()>0){
                CriteriaBuilder.In<String> in = builder.in(root.get("id").as(String.class));
                for (String id : body.getCampaignIds()) {
                    in.value(id);
                }
                predicates.add(in);
            }

            String operate = body.getOperate();
            String status = body.getStatus();
            if(StringUtils.hasLength(operate)){
                if(operate.equals("manage") && !StringUtils.hasLength(status)){ // 管理员 并且没有查询具体状态时 去掉状态是取消的活动
                    predicates.add(builder.notEqual(root.get("status").as(String.class), CampaignStatus.CANCELLED.getKey()));
                }
            }

            String creatorId = body.getCreatorId();
            if (StringUtils.hasLength(creatorId)) {
                predicates.add(builder.equal(root.get("creatorId").as(String.class), creatorId));
            }

            if (StringUtils.hasLength(status)) {
                predicates.add(builder.equal(root.get("status").as(String.class), status));
            }

            String auditsStatus = body.getAuditsStatus();
            if (StringUtils.hasLength(auditsStatus)) {
                predicates.add(builder.equal(root.get("auditsStatus").as(String.class), auditsStatus));
//                predicates.add(builder.notEqual(root.get("status").as(String.class), Constant.CampaignStatus.CANCELLED.getKey()));
            }
            return builder.and(predicates.toArray(new Predicate[0]));
        };
        Page<CampaignPO> pageAll = campaignRepository.findAll(specification, pageable);
        List<CampaignPO> all = pageAll.getContent();
        for (CampaignPO campaignPO : all) {

            list.add(getCampaignInfo(campaignPO, true));
        }
        return pageAll.getTotalElements();
    }

    @Override
    public List<Status> findAllStatus(CampaignSearch body){

        List<Status> statusList = new LinkedList<>();
        List<CampaignInfo> all = new ArrayList<>();
        body.setPageNum(1);
        body.setPageSize(500);
        this.findAll(body, all);

        statusList.add(new Status().key("ALL").name("全部").value(all.size()));
        Map<String, Long> auditCollect = all.stream().collect(Collectors.groupingBy(CampaignInfo::getAuditsStatus, Collectors.counting()));
        AuditStatus[] auditStatusAll = AuditStatus.values();
        for (AuditStatus auditStatus:auditStatusAll){
            if(!auditStatus.getKey().equals("ALL")){
                Status status = adapterStatus(auditCollect, auditStatus.getKey(), auditStatus.getName());
                statusList.add(status);
            }
        }

        Map<String, Long> collect = all.stream().collect(Collectors.groupingBy(CampaignInfo::getStatus, Collectors.counting()));
        CampaignStatus[] statusAll = CampaignStatus.values();
        for (CampaignStatus campaignStatus:statusAll){
            Status status = adapterStatus(collect, campaignStatus.getKey(), campaignStatus.getName());
            statusList.add(status);
        }

        return statusList;
    }

    /**
     * 报名
     * @param body
     */
    @Transactional
    @Override
    public void signUpCampaignPost(List<CampaignUserInfo> body) {
        HashMap<String, Boolean> map = new HashMap<>();
        for(CampaignUserInfo campaignUserInfo:body){

            UserPO userPO = userRepository.getUserPOById(campaignUserInfo.getUserId());
            CampaignPO campaignPO = campaignRepository.findCampaignPOById(campaignUserInfo.getCampaignId());
            Integer score = userPO.getScore();
            Integer singUpNum = 0;
            if(!map.containsKey(campaignUserInfo.getUserId())){
                List<CampaignUserPO> exit = campaignUserRepository.findAllByUserIdAndCampaignId(campaignUserInfo.getUserId(), campaignUserInfo.getCampaignId());
                if(exit.size() == 0){
                    if(campaignUserInfo.getType().equals("volunteer")){
                        score = userPO.getScore() + campaignPO.getVolunteerScore();
                    } else {
                        score = userPO.getScore() - campaignPO.getConsumeScore();
                    }
                    map.put(campaignUserInfo.getUserId(), true);
                }
            }
            if(campaignUserInfo.getType().equals("volunteer")){
                singUpNum = campaignPO.getVolunteerNum();
            } else {
                singUpNum = campaignPO.getRecruitNum();
            }

            if(score < 0){
                throw new RuntimeException("用户("+userPO.getName()+")积分不足,报名失败");
            }

            List<CampaignUserPO> signUpList = campaignUserRepository.findAllByCampaignIdAndType(campaignUserInfo.getCampaignId(), campaignUserInfo.getType());
            if(signUpList.size() >= singUpNum) {
                throw new RuntimeException("报名人数达到上限,报名失败");
            }

            List<CampaignUserPO> exit = campaignUserRepository.findAllByUserIdAndCampaignIdAndUserChildId(campaignUserInfo.getUserId(), campaignUserInfo.getCampaignId(), campaignUserInfo.getUserChildId());
            log.info("exit:"+exit.size());
            if(exit.size() == 0){
                userPO.setScore(score);
                entityManager.merge(userPO);

                List<String> ignorePropList = new ArrayList<>();
                ignorePropList.add("dateCreated");
                ignorePropList.add("lastUpdated");
                String[] ignoreProps = new String[ignorePropList.size()];
                ignorePropList.toArray(ignoreProps);

                CampaignUserPO campaignUserPO = new CampaignUserPO();
                BeanUtils.copyProperties(campaignUserInfo, campaignUserPO, ignoreProps);
                if(StringUtils.hasLength(campaignUserPO.getUserChildId())){
                    campaignUserPO.setUserType("child");
                } else {
                    campaignUserPO.setUserType("parent");
                }

                campaignPO.setDateCreated(DateUtils.now());
                campaignPO.setLastUpdated(DateUtils.now());
                entityManager.merge(campaignUserPO);

                entityManager.flush();
            }
        }
    }

    /**
     * 取消报名
     * @param body
     */
    @Transactional
    @Override
    public void cancelSignUpCampaignPost(List<CampaignUserInfo> body) {
        for(CampaignUserInfo campaignUserInfo:body){
            if(campaignUserInfo.getId() == null){
                throw new RuntimeException("用户id不可为空");
            }

            CampaignUserPO campaignUserPO = campaignUserRepository.findCampaignUserPOById(campaignUserInfo.getId());

            List<CampaignUserPO> exit = campaignUserRepository.findAllByUserIdAndCampaignIdAndIdNot(campaignUserPO.getUserId(),
                    campaignUserPO.getCampaignId(), campaignUserPO.getId());

            if(exit.size() == 0){
                CampaignPO campaignPO = campaignRepository.findCampaignPOById(campaignUserPO.getCampaignId());
                UserPO userPO = userRepository.getUserPOById(campaignUserPO.getUserId());
                Integer score = 0;
                if(campaignUserPO.getType().equals("volunteer")){
                    score = userPO.getScore() - campaignPO.getVolunteerScore();
                } else {
                    score = userPO.getScore() + campaignPO.getConsumeScore();
                }

                userPO.setScore(score);
                entityManager.merge(userPO);
                entityManager.flush();
            }
            campaignUserRepository.deleteById(campaignUserPO.getId());
        }

    }

    /**
     * 添加 活动拓展信息
     */
    @Transactional
    @Override
    public void addExpansionPost(CampaignUserInfo campaignUserInfo){

        CampaignUserPO campaignUserPO = campaignUserRepository.findCampaignUserPOByUserIdAndCampaignIdAndUserType(campaignUserInfo.getUserId(), campaignUserInfo.getCampaignId(), "parent");
        String content = null;
        String type = null;
        if(StringUtils.hasLength(campaignUserInfo.getPhoto())){
            type = "photo";
            content = campaignUserInfo.getPhoto();
//            campaignUserPO.setPhoto(campaignUserInfo.getPhoto());
        }
        if(StringUtils.hasLength(campaignUserInfo.getVideo())){
            type = "video";
            content = campaignUserInfo.getVideo();
//            campaignUserPO.setVideo(campaignUserInfo.getVideo());
        }
        if(StringUtils.hasLength(campaignUserInfo.getContribution())){
            type = "contribution";
            content = campaignUserInfo.getContribution();
//            campaignUserPO.setContribution(campaignUserInfo.getContribution());
        }
//        campaignUserPO.setLastUpdated(DateUtils.now());
//        entityManager.merge(campaignUserPO);

        CampaignExpansionPO campaignExpansionPO = campaignExpansionRepository.findCampaignExpansionPOByCampaignIdAndUserIdAndType(campaignUserInfo.getCampaignId(),campaignUserInfo.getUserId(), type);

        if(campaignExpansionPO == null){
            campaignExpansionPO = new CampaignExpansionPO();
            campaignExpansionPO.setCampaignUserId(campaignUserPO.getId());
            campaignExpansionPO.setUserId(campaignUserPO.getUserId());
            campaignExpansionPO.setCampaignId(campaignUserPO.getCampaignId());
            campaignExpansionPO.setType(type);
            campaignExpansionPO.setDateCreated(DateUtils.now());
        }

        campaignExpansionPO.setContent(content);
        campaignExpansionPO.setLastUpdated(DateUtils.now());
        entityManager.merge(campaignExpansionPO);

        entityManager.flush();
    }

    /**
     * 添加 用户积分
     */
    @Transactional
    @Override
    public void userAddScorePost(UserScoreInfo userScoreInfo) {
        CampaignExpansionPO campaignExpansionPO = campaignExpansionRepository.findCampaignExpansionPOByCampaignIdAndUserIdAndType(userScoreInfo.getCampaignId(),userScoreInfo.getUserId(), userScoreInfo.getType());

        if(campaignExpansionPO==null){
            throw new RuntimeException("用户未上传该类型的活动拓展信息，请用户上传后在为其加分！");
        }
        Integer oldScore = campaignExpansionPO.getScore();
        campaignExpansionPO.setScore(userScoreInfo.getScore());
        campaignExpansionPO.setLastUpdated(DateUtils.now());
        entityManager.merge(campaignExpansionPO);

        UserPO userPO = userRepository.getUserPOById(userScoreInfo.getUserId());
        Integer score = userScoreInfo.getScore() + userPO.getScore() - oldScore;
        userPO.setScore(score);
        userPO.setLastUpdated(DateUtils.now());
        entityManager.merge(userPO);
        entityManager.flush();
    }

    /**
     * 文件上传 活动拓展信息
     * @param body
     */
    @Transactional
    @Override
    public void addExpansionFilePost(CampaignFile body) {
        CampaignExpansionPO campaignExpansionPO = campaignExpansionRepository.findCampaignExpansionPOByCampaignIdAndUserIdAndType(body.getCampaignId(),body.getUserId(), body.getType());
        CampaignUserPO campaignUserPO = campaignUserRepository.findCampaignUserPOByUserIdAndCampaignIdAndUserType(body.getUserId(), body.getCampaignId(), "parent");

        // 保存新文件
        String newPath = fileUtilService.uploadFile(body.getFile(), "campaignExpansion");
        String fileName = body.getFile().getOriginalFilename();
        // 保存数据
        if(campaignExpansionPO == null){
            campaignExpansionPO = new CampaignExpansionPO();
            campaignExpansionPO.setCampaignUserId(campaignUserPO.getId());
            campaignExpansionPO.setUserId(campaignUserPO.getUserId());
            campaignExpansionPO.setCampaignId(campaignUserPO.getCampaignId());
            campaignExpansionPO.setType(body.getType());
            campaignExpansionPO.setDateCreated(DateUtils.now());
        }

        String oldPath = campaignExpansionPO.getContent();

        campaignExpansionPO.setFileName(fileName);
        campaignExpansionPO.setContent(newPath);
        campaignExpansionPO.setLastUpdated(DateUtils.now());
        entityManager.merge(campaignExpansionPO);
        entityManager.flush();

        if(StringUtils.hasLength(oldPath)) { // 删除旧文件如果有的话
            fileUtilService.delFile(oldPath);
        }

    }

    private Status adapterStatus(Map<String, Long> collect, String key, String name){
        int value = 0;
        if(collect.containsKey(key)){
            value = collect.get(key).intValue();
        }
        Status status = new Status().key(key).name(name).value(value);
        return status;
    }

    private CampaignInfo getCampaignInfo(CampaignPO campaignPO, Boolean withUser) {
        CampaignInfo info = new CampaignInfo();
        BeanUtils.copyProperties(campaignPO, info);

        UserPO userPO = userRepository.getUserPOById(campaignPO.getCreatorId());
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(userPO, userInfo);

        info.setCreator(userInfo); // 活动创建人信息

        if(withUser){
            List<CampaignUserPO> campaignUserPOS = campaignUserRepository.findAllByCampaignId(info.getId());
            List<CampaignUserInfo> campaignUserInfos = new LinkedList<>();
            for(CampaignUserPO campaignUserPO:campaignUserPOS){
                CampaignUserInfo campaignUserInfo = new CampaignUserInfo();
                BeanUtils.copyProperties(campaignUserPO, campaignUserInfo);

                UserPO userPO1 = userRepository.getUserPOById(campaignUserInfo.getUserId());
                UserInfo userInfo1 = new UserInfo();
                BeanUtils.copyProperties(userPO1, userInfo1);
                campaignUserInfo.setUserInfo(userInfo1); // 家长信息

                if(StringUtils.hasLength(campaignUserInfo.getUserChildId())){
                    UserChildPO userChildPO = userChildRepository.getUserChildPOById(campaignUserInfo.getUserChildId());
                    UserChildInfo userChildInfo = new UserChildInfo();
                    BeanUtils.copyProperties(userChildPO, userChildInfo);
                    campaignUserInfo.setUserChildInfo(userChildInfo); // 孩子信息
                }

                List<CampaignExpansionPO> campaignExpansionPOs = campaignExpansionRepository.findAllByCampaignUserId(campaignUserPO.getId());
                List<CampaignExpansionInfo> campaignExpansionInfos = new ArrayList<>();
                for(CampaignExpansionPO campaignExpansionPO:campaignExpansionPOs){
                    CampaignExpansionInfo campaignExpansionInfo = new CampaignExpansionInfo();
                    BeanUtils.copyProperties(campaignExpansionPO, campaignExpansionInfo);
                    campaignExpansionInfos.add(campaignExpansionInfo);
                }
                campaignUserInfo.setCampaignExpansionInfos(campaignExpansionInfos); // 用户上传的拓展信息

                campaignUserInfos.add(campaignUserInfo);
            }
            info.setCampaignUserInfos(campaignUserInfos);
        }

        return info;
    }

    @Override
    @Transactional
    public void changeStatus(List<Body> bodies) {
        for (Body body : bodies) {
            String id = body.getId();
            if (!StringUtils.hasLength(id)) {
                throw new RuntimeException("ID不能为空");
            }
            String status = body.getStatus();
            if (!StringUtils.hasLength(status)) {
                throw new RuntimeException("状态不能为空");
            }
            // 校验用户状态有效
            CampaignStatus.getNameByKey(status);
            Optional<CampaignPO> byId = campaignRepository.findById(id);
            if (!byId.isPresent()) {
                throw new RuntimeException("活动不存在！");
            }
            CampaignPO campaignPO = byId.get();

            campaignPO.setStatus(status);
            if(status.equals(CampaignStatus.CANCELLED.getKey())){
                campaignPO.setReason(body.getReason());
                campaignPO.setAuditsStatus(AuditStatus.NOAUDIT.getKey());
            }
            campaignPO.setLastUpdated(DateUtils.now());
            entityManager.merge(campaignPO);

            // 添加用户积分
            if(status.equals(CampaignStatus.COMPLETED.getKey())){
                UserPO userPO = userRepository.getUserPOById(campaignPO.getCreatorId());
                Integer score = userPO.getScore() + 10;
                userPO.setScore(score);
                entityManager.merge(userPO);
            }
        }
        entityManager.flush();
    }

    @Override
    @Transactional
    public void changeAuditStatus(List<Body> bodies) {
        for (Body body : bodies) {
            String id = body.getId();
            if (!StringUtils.hasLength(id)) {
                throw new RuntimeException("ID不能为空");
            }
            String status = body.getStatus();
            if (!StringUtils.hasLength(status)) {
                throw new RuntimeException("审核状态不能为空");
            }
            // 校验用户状态有效
            AuditStatus.getNameByKey(status);
            Optional<CampaignPO> byId = campaignRepository.findById(id);
            if (!byId.isPresent()) {
                throw new RuntimeException("活动不存在！");
            }
            CampaignPO campaignPO = byId.get();
            campaignPO.setAuditsStatus(status);
            if(status.equals("PASS")){
                campaignPO.setStatus(CampaignStatus.RECRUITING.getKey());
            } else {
                campaignPO.setReason(body.getReason());
                campaignPO.setStatus(CampaignStatus.REFUSE.getKey());
            }
            campaignPO.setLastUpdated(DateUtils.now());
            entityManager.merge(campaignPO);
        }
        entityManager.flush();
    }

    @Override
    public CampaignInfo findById(String id, Boolean withUser){
        Optional<CampaignPO> byId = campaignRepository.findById(id);
        CampaignPO campaignPO = byId.get();

        CampaignInfo campaignInfo = getCampaignInfo(campaignPO, withUser);
        return campaignInfo;
    }
}
